{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2a2294a7",
   "metadata": {},
   "source": [
    "# Using Jupyter to write FastHTML\n",
    "\n",
    "> Writing FastHTML applications in Jupyter notebooks requires a slightly different process than normal Python applications.\n",
    "\n",
    "- skip_exec: true"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4b1ee344",
   "metadata": {},
   "source": [
    "Writing FastHTML applications in Jupyter notebooks requires a slightly different process than normal Python applications. \n",
    "\n",
    ":::{.callout-tip title='Download this notebook and try it yourself'}\n",
    "The source code for this page is a [Jupyter notebook](https://github.com/AnswerDotAI/fasthtml/blob/main/nbs/tutorials/jupyter_and_fasthtml.ipynb). That makes it easy to directly experiment with it. However, as this is working code that means we have to comment out a few things in order for the documentation to build.\n",
    ":::\n",
    "\n",
    "The first step is to import necessary libraries. As using FastHTML inside a Jupyter notebook is a special case, it remains a special import. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e244a34a",
   "metadata": {},
   "outputs": [],
   "source": [
    "from fasthtml.common import *\n",
    "from fasthtml.jupyter import JupyUvi, HTMX"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f38039a",
   "metadata": {},
   "source": [
    "Let's create an app with `fast_app`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9ac00db7",
   "metadata": {},
   "outputs": [],
   "source": [
    "app, rt = fast_app(pico=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d24f2972",
   "metadata": {},
   "source": [
    "Define a route to test the application."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ea60bc88",
   "metadata": {},
   "outputs": [],
   "source": [
    "@rt\n",
    "def index():\n",
    "    return Titled('Hello, Jupyter',\n",
    "           P('Welcome to the FastHTML + Jupyter example'),\n",
    "           Button('Click', hx_get='/click', hx_target='#dest'),\n",
    "           Div(id='dest')\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e512dbab",
   "metadata": {},
   "source": [
    "Create a `server` object using `JupyUvi`, which also starts Uvicorn. The `server` runs in a separate thread from Jupyter, so it can use normal HTTP client functions in a notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3b815c60",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "<script>\n",
       "document.body.addEventListener('htmx:configRequest', (event) => {\n",
       "    if(event.detail.path.includes('://')) return;\n",
       "    htmx.config.selfRequestsOnly=false;\n",
       "    event.detail.path = `${location.protocol}//${location.hostname}:8000${event.detail.path}`;\n",
       "});\n",
       "</script>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "server = JupyUvi(app)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7341b10a",
   "metadata": {},
   "source": [
    "The `HTMX` callable displays the server's HTMX application in an iframe which can be displayed by Jupyter notebook. Pass in the same `port` variable used in the `JupyUvi` callable above or leave it blank to use the default (8000).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e7d4e6fa",
   "metadata": {},
   "outputs": [],
   "source": [
    "# This doesn't display in the docs - uncomment and run it to see it in action\n",
    "# HTMX()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd772e31",
   "metadata": {},
   "source": [
    "We didn't define the `/click` route, but that's fine - we can define (or change) it any time, and it's dynamically inserted into the running app. No need to restart or reload anything!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3ec36d1e",
   "metadata": {},
   "outputs": [],
   "source": [
    "@rt\n",
    "def click(): return P('You clicked me!')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "72fc8a55",
   "metadata": {},
   "source": [
    "## Full screen view"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4cb6bb90",
   "metadata": {},
   "source": [
    "You can view your app outside of Jupyter by going to `localhost:PORT`, where `PORT` is usually the default 8000, so in most cases just click [this link](localhost:8000/)."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b231b0b0",
   "metadata": {},
   "source": [
    "## Graceful shutdowns"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86554c9d",
   "metadata": {},
   "source": [
    "Use the `server.stop()` function displayed below. If you restart Jupyter without calling this line the thread may not be released and the `HTMX` callable above may throw errors. If that happens, a quick temporary fix is to specify a different port number in JupyUvi and HTMX with the `port` parameter.\n",
    "\n",
    "Cleaner solutions to the dangling thread are to kill the dangling thread (dependant on each operating system) or restart the computer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6650d9bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "server.stop()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "python3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
